package com.huajx.core.config;

import com.huajx.util.*;
import com.huajx.util.collection.CollectionUtil;
import com.huajx.util.map.MapUtil;
import com.huajx.util.properties.Props;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class HbootConfigManager {
    private static Logger logger = LoggerFactory.getLogger(HbootConfigManager.class);

    private Props mainProperties;
    private HbootConfig config;

    private Map<String, Object> configs = new ConcurrentHashMap<>();

    private static HbootConfigManager instance;

    public static HbootConfigManager me() {
        if (instance == null) {
            instance = new HbootConfigManager();
        }
        return instance;
    }

    private HbootConfigManager() {
        init();
    }

    private void init() {
        readLocalConfig();
    }

    /**
     * 读取本地配置文件
     */
    private void readLocalConfig() {
        try {
            mainProperties = Props.getProp("hboot.properties", CharsetUtil.CHARSET_UTF_8);//PropKit.use("jboot.properties");
        } catch (Throwable ex) {
            logger.warn("Could not find hboot.properties in your class path.");
            mainProperties = new Props();
        }

        config = get(HbootConfig.class);

    }

    public <T> T get(Class<T> clazz) {
        PropertyConfig propertyConfig = clazz.getAnnotation(PropertyConfig.class);
        if (propertyConfig == null) {
            return get(clazz, null, null);
        }
        return get(clazz, propertyConfig.prefix(), propertyConfig.file());
    }

    /**
     * 获取配置信息，并创建和赋值clazz实例
     *
     * @param clazz  指定的类
     * @param prefix 配置文件前缀
     * @param <T>    返回对象类型
     * @return
     */
    public <T> T get(Class<T> clazz, String prefix, String file) {

        T obj = (T) configs.get(clazz.getName() + prefix);
        if (obj == null) {
            synchronized (clazz) {
                if (obj == null) {
                    obj = newConfigObject(clazz, prefix, file);
                    configs.put(clazz.getName() + prefix, obj);
                }
            }
        }

        return obj;
    }

    private final static Map<String, Map<String, Object>> configMaps = new HashMap<>();

    /**
     * 获取配置信息，根据配置类和配置扩展信息
     *
     * @param clazz
     * @param suffix
     * @param <T>
     * @return
     */
    public <T> Map<String, T> get(Class<T> clazz, String suffix) {
        String className = clazz.getName();
        String configMapsKey = StrUtil.wrap(className, null, suffix);

        Map<String, T> results = (Map<String, T>) configMaps.get(configMapsKey);

        if (ObjectUtil.notEmpty(results)) {
            return results;
        }

        PropertyConfig propertyConfig = clazz.getAnnotation(PropertyConfig.class);
        String prefix = null;
        String file = null;

        if (propertyConfig != null) {
            prefix = propertyConfig.prefix();
            file = propertyConfig.file();
        }

        if (StrUtil.isBlank(suffix)) {
            results = MapUtil.of(className + prefix, get(clazz));

            configMaps.put(configMapsKey, (Map<String, Object>) results);

            return results;
        }

        Set<String> names = mainProperties.stringPropertyNames(StrUtil.join(StrUtil.DOT, prefix, suffix));

        Set<String> keys = CollectionUtil.newHashSet();

        results = MapUtil.newHashMap();

        for (String name : names) {
            String key = StrUtil.subBefore(name, CharUtil.DOT, true);

            if (results.containsKey(key)) {
                continue;
            }

            results.put(key, get(clazz, key, file));
        }

        configMaps.put(configMapsKey, (Map<String, Object>) results);

        return results;
    }

    private <T> T newConfigObject(Class<T> clazz, String prefix, String file) {
        Field[] fields = ReflectUtil.getFields(clazz);

        T obj = ReflectUtil.newInstance(clazz);

        if (ArrayUtil.isEmpty(fields)) {
            return obj;
        }

        if (StrUtil.isNotBlank(file)) {
            Props prop = Props.getProp(file, CharsetUtil.CHARSET_UTF_8);
            mainProperties.putAll(prop);
        }

        for (Field field : fields) {
            String fieldName = field.getName();

            String key = getKey(prefix, fieldName);
            try {
                Object value = mainProperties.get(key);
                if (ObjectUtil.isNotNull(value)) {
                    ReflectUtil.setFieldValue(obj, field, value);
                }
            } catch (Exception e) {
            }
        }

        return obj;
    }

    private String getKey(String prefix, String key) {
        if (StrUtil.isBlank(prefix)) {
            return key;
        }
        return prefix.trim() + "." + key;
    }
}
